Skip to content

消息队列_RabbitMQ_基础概念

大纲

  • RabbitMQ是什么
  • RabbitMQ的组成部分
  • RabbitMQ 的工作流程
  • RabbitMQ广播和直接模式示例

一、基础概念

“消息队列(Message Queue)”是在消息的传输过程中保存消息的容器

MQ 的核心作用:削峰、解耦、异步

RabbitMQ是什么

建议直接去官网看一下相关概念: https://www.rabbitmq.com/documentation.html

RabbitMQ的特点

RabbitMQ是一款使用Erlang语言开发的,实现AMQP(高级消息队列协议)的开源消息中间件。

特点

  • 可靠性。支持持久化,传输确认,发布确认等保证了MQ的可靠性。
  • 灵活的分发消息策略。这应该是RabbitMQ的一大特点。在消息进入MQ前由Exchange(交换机)进行路由消息。分发消息策略有:简单模式、工作队列模式、发布订阅模式、路由模式、通配符模式。
  • 支持集群。多台RabbitMQ服务器可以组成一个集群,形成一个逻辑Broker。
  • 多种协议。RabbitMQ支持多种消息队列协议,比如 STOMP、MQTT 等等。
  • 支持多种语言客户端。RabbitMQ几乎支持所有常用编程语言,包括 Java、.NET、Ruby 等等。
  • 可视化管理界面。RabbitMQ提供了一个易用的用户界面,使得用户可以监控和管理消息 Broker。
  • 插件机制。RabbitMQ提供了许多插件,可以通过插件进行扩展,也可以编写自己的插件。

安装和使用

    1. 安装 erlang 环境
    1. 安装RabbitMQ服务端

RabbitMQ的组成部分

核心概念

  • Broker:消息队列服务进程。此进程包括两个部分:Exchange和Queue。
    • Exchange:消息队列交换机。按一定的规则将消息路由转发到某个队列
    • Queue:消息队列,存储消息的队列。
  • Producer:消息生产者。生产方客户端将消息同交换机路由发送到队列中。
  • Consumer:消息消费者。消费队列中存储的消息。

大概流程:

image.png

  • 消息生产者连接到RabbitMQ Broker,创建connection,开启channel。
  • 生产者声明交换机类型、名称、是否持久化等。
  • 生产者发送消息,并指定消息是否持久化等属性和 routing key。
  • exchange收到消息之后,根据routing key路由到跟当前交换机绑定的相匹配的队列里面。
  • 消费者监听接收到消息之后开始业务处理
生产者 --[消息]--> 交换机 --[路由]--> 队列 --[消费]--> 消费者
    \                                         /
     \--[通道 Channel]-----------------------/

通道的概念:

  • 定义:通道是建立在实际的 TCP 连接内的一个轻量级连接。它是执行大多数操作(如消息发布或消息接收)的路径。
  • 作用:通道是多路复用连接中的一个独立路径,允许多个通道共享一个 TCP 连接,而不必为每个通道建立物理连接。这提高了资源利用率和通信效率
  • 生产者使用通道来发送消息到交换机,而消费者使用通道从队列中接收消息。通道作为消息传输的通道,连接了生产者、交换机和消费者。

通道是作为消息传递的载体贯穿整个过程。

RabbitMQ 的工作流程

工作流程

image.png

  • 建立连接
  • 生产者
    • 声明交换机类型、名称、是否持久化等
    • 发送消息
  • 交换机
    • 交换机接收消息,进行消息路由
  • 消费者
    • 订阅消息(监听队列)
    • 接收消息,业务处理

一、消息生产

  • 创建连接
    • 生产者通过客户端库与RabbitMQ服务器(Broker)建立连接(Connection),并在连接上创建一个信道(Channel)。
  • 创建消息(声明交换机,指定 routing key)
    • 生产者(Producer)是消息的发送方,它创建包含数据的消息,并指定一个routing key来描述这条消息的路由规则。
  • 发送消息
    • 生产者将消息发送到一个指定的交换机(Exchange),并附上routing key。交换机类型和routing key共同决定了消息的路由方式。

二、消息路由

  • 交换机接收消息
    • 交换机根据自身类型(如Direct, Fanout, Topic, Headers)和生产者指定的routing key来决定如何处理接收到的消息。
  • 路由队列
    • 交换机根据与其绑定的队列(Queue)的binding key来决定消息应该被路由到哪些队列。
    • 对于Direct和Topic交换机,这一过程涉及到routing keybinding key的匹配。

三、消息存储

  • 消息入队
    • 一旦决定了消息的目标队列,消息将被存储在队列中等待被消费。队列保证了消息的有序性和冗余存储。
  • 持久化(可选)
    • 如果队列被声明为持久化的,并且消息也被标记为持久化,那么消息会被写入磁盘,以确保即使在RabbitMQ重启后消息也不会丢失。

四、消息消费

  • 消费者订阅队列
    • 消费者(Consumer)通过客户端库与RabbitMQ建立连接,并创建信道。然后它订阅一个或多个队列,准备接收消息。
  • 消息发送给消费者
    • RabbitMQ将队列中的消息按顺序传递给订阅该队列的消费者。
    • 如果有多个消费者订阅同一个队列,RabbitMQ可以按照轮询或其他策略分配消息,以实现负载均衡。
  • 消息确认
    • 消费者处理完消息后,需要向RabbitMQ发送一个确认(ACK)信号,表示消息已经被正确处理。
    • 如果消费者因为某些原因无法处理消息,它可以发送拒绝(NACK)信号或不确认(Reject),让RabbitMQ根据配置进行消息重试或将消息发送到死信队列。

五、消息确认与重试

  • 手动与自动确认
    • 消费者可以选择手动确认消息或配置自动确认。手动确认给予消费者更大的控制权,但需要在消费者代码中显式处理。
  • 失败重试和死信处理
    • 对于处理失败的消息,消费者可以通过NACK或Reject并设置重入队列标志来实现重试。或者通过死信交换机(DLX)机制将无法处理的消息转发到死信队列。
交换机类型

(交换机 → 队列)

  • Direct Exchange
    • binding key 与消息的 routing key 完全匹配队列
  • Topic Exchange
    • 模式匹配
  • Fanout Exchange
    • 广播(忽略 routing key
  • Headers Exchange
    • 不依赖 routing key,头部属性匹配

RabbitMQ 中最常用的几种交换机类型及其区别:

  1. Direct Exchange(直接交换机)
  • 行为:消息被路由到那些 binding key 与消息的 routing key 完全匹配的队列。
  • 用途:非常适合单播(unicast)或多播(multicast)路由场景,即一对一或一对多发送。
  1. Fanout Exchange(扇出交换机)
  • 行为:消息被路由到所有与该交换机绑定的队列,忽略 routing key
  • 用途:非常适合广播消息,如日志系统,其中消息需要被发送到多个目的地。
  1. Topic Exchange(主题交换机)
  • 行为:可以根据模式匹配 routing keysbinding keys(模式中可以包含通配符)。routing key 为点分隔的一系列单词,binding key 中可以包含特殊字符 *# 来进行模式匹配,其中 * 匹配一个单词,# 匹配零个或多个单词。
  • 用途:适用于同时需要单播和多播路由逻辑的复杂路由配置。
  1. Headers Exchange(头部交换机)
  • 行为:基于消息头部(headers)中的属性进行匹配。不依赖 routing key。匹配规则可以是“所有”(x-match=all,即所有头部属性必须匹配)或“任何”(x-match=any,即任意头部属性匹配)。
  • 用途:适用于需要根据多个属性进行路由决策的高级路由策略。

总结

选择哪种交换机类型主要取决于你的具体应用场景和消息路由需求。

直接交换机适合简单的单播路由,扇出交换机适合广播,主题交换机适合复杂的路由场景,而头部交换机适合基于多属性的路由决策。了解这些交换机的特点可以帮助你更好地设计消息路由策略,并充分利用 RabbitMQ 的强大功能。

RabbitMQ 的处理流程是围绕着连接、交换机、消息发送和消息接收这几个核心环节展开的。

路由键(Routing Key)的作用

路由键是消息发布到 RabbitMQ 时附带的一个字符串标识,它的作用是帮助交换机决定如何路由消息。

这里的“路由”指的是决定消息应该被发送到哪一个或哪些队列。路由键的具体作用取决于交换机的类型:

  1. Direct Exchange:交换机会将消息路由到那些 binding keyrouting key 完全匹配的队列。
  2. Topic Exchange:可以进行模式匹配,交换机会根据 routing key 和队列的 binding key(可以包含通配符)进行匹配。
  3. Fanout Exchange:忽略路由键,消息会被发送到所有绑定到该交换机的队列。
  4. Headers Exchange:不依赖路由键,而是根据发送的消息头部中的键值对进行匹配。

routing key 和 binding key 的作用

  • routing key
    • 1、生产者发送消息的时候附加的一个字符串标识符
    • 2、这个标识符的作用是:描述消息的路由规则,决定了这个消息应该路由到哪个队列
  • binding key
    • 1、队列绑定交换机的时候声明的一个字符串标识符
    • 2、这个标识符的作用取决于交换机的类型和预期的消息路由逻辑

单独抽离 消息队列_RabbitMQ_交换机类型#1、RabbitMQ广播和直接模式示例 中关于 Direct Exchange 直接交换机的使用内容:

直接交换机是一种将消息路由到那些binding key与消息的routing key完全匹配的队列的交换机。

RabbitConfig 配置类内容

声明 Direct 交换机

    /**
     * 声明Direct交换机 支持持久化.
     * 通过directExchange方法创建一个名为DIRECT_EXCHANGE的Direct交换机,并设置为支持持久化,这意味着交换机会在RabbitMQ重启后依然存在。
     *
     * @return the exchange
     */
    @Bean("directExchange")
    public Exchange directExchange() {
        return ExchangeBuilder.directExchange("DIRECT_EXCHANGE").durable(true).build();
    }

声明一个队列

    /**
     * 声明一个队列 支持持久化.
     *directQueue方法声明了一个名为DIRECT_QUEUE的队列,并设置为持久化。
     * @return the queue
     */
    @Bean("directQueue")
    public Queue directQueue() {
        return QueueBuilder.durable("DIRECT_QUEUE").build();
    }

绑定队列到交换机

    /**
     * 通过绑定键 将指定队列绑定到一个指定的交换机 .
     *directBinding方法通过BindingBuilder将DIRECT_QUEUE队列绑定到DIRECT_EXCHANGE交换机,绑定键为DIRECT_ROUTING_KEY。
     * 这意味着只有当消息的routing key为DIRECT_ROUTING_KEY时,消息才会被路由到DIRECT_QUEUE队列。
     * @param queue    the queue
     * @param exchange the exchange
     * @return the binding
     */
    @Bean
    public Binding directBinding(@Qualifier("directQueue") Queue queue,
                                 @Qualifier("directExchange") Exchange exchange) {
        return BindingBuilder.bind(queue).to(exchange).with("DIRECT_ROUTING_KEY").noargs();
    }

发送测试

    /**
     * 测试Direct模式.
     *
     * @param p the p
     * @return the response entity
     */
    public void direct(String p) {
        CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
        rabbitTemplate.convertAndSend("DIRECT_EXCHANGE", "DIRECT_ROUTING_KEY", p, correlationData);
    }

交换机与消费者之间的协同

交换机不直接与消费者通信来决定向哪个消费者发送消息。

相反,它依赖于队列和队列与消费者之间的关系来实现消息的分发。下面是它们是如何协同工作的:

  1. 队列绑定到交换机:队列通过 binding key(在 fanout 和 headers 交换机中,这个 binding key 不一定被使用)绑定到交换机。这个过程决定了哪些消息会被路由到特定的队列。
  2. 消费者监听队列:消费者通过打开一个通道并订阅(或监听)一个或多个队列来接收消息。这意味着消费者告诉 RabbitMQ:“我对这个队列中的消息感兴趣,请将它们发送给我。”
  3. 消息分发:当消息通过交换机被路由到队列后,RabbitMQ 会将队列中的消息发送给订阅该队列的消费者。这个分发过程可能是基于不同的策略,如轮询或公平调度等。
消费者的监听

当我们说消费者“监听”一个队列时,实际上是指消费者开启了一个持续的、异步的过程,这个过程会等待、接收并处理来自队列的消息。

消费者通过通道向 RabbitMQ 注册其对特定队列的兴趣。一旦队列中有消息,RabbitMQ 就会将消息推送给消费者。消费者接收到消息后,会根据自己的逻辑进行处理。

消费者通过监听队列来接收消息,交换机本身并不决定消息发送给哪个消费者,这是由队列中的消息与消费者之间的关系决定的。消费者监听意味着它们准备好接收和处理从队列中来的消息。

二、常见面试题

常见生产问题以及如何处理和常见面试题

消息队列_RabbitMQ_基础 - 常见面试题

  • 如何消息不丢失
  • 如何避免消息重复消费
  • 如何处理消息堆积问题
  • 讲述一下 RabbitMQ 的死信队列
  • 讲述一下 RabbitMQ 的延迟队列

三、高可用机制

在生产环境下,使用集群来保证高可用性

分为:普通集群、镜像集群、仲裁集群

普通集群

生产环境一般不使用普通集群

普通集群,或者叫标准集群(classic cluster ),具备下列特征:

  • 会在集群的各个节点间共享部分数据,包括:交换机、队列元信息。不包含队列中的消息。
  • 当访问集群某节点时,如果队列不在该节点,会从数据所在节点传递到当前节点并返回
  • 队列所在节点宕机,队列中的消息就会丢失

镜像集群

镜像集群:本质是主从模式,具备下面的特征:

  • 交换机、队列、队列中的消息会在各个 mq 的镜像节点之间同步备份。
  • 创建队列的节点被称为该队列的主节点,备份到的其它节点叫做该队列的镜像节点。
  • 一个队列的主节点可能是另一个队列的镜像节点
  • 所有操作都是主节点完成,然后同步给镜像节点
  • 主宕机后,镜像节点会替代成新的主节点

仲裁队列

仲裁队列:仲裁队列是3.8版本以后才有的新功能,用来替代镜像队列,具备下列特征:

  • 与镜像队列一样,都是主从模式,支持主从数据同步
  • 使用非常简单,没有复杂的配置
  • 主从同步基于Raft协议,强一致 ·

面试题回答:RabbitMQ 的高可用机制有了解过吗

image.png

参考回答;

image.png


参考